﻿/********************************************************************************
 *    Project   : Awesomium.NET (SafeBrowser)
 *    File      : MainWindow.cs
 *    Version   : 1.7.0.0 
 *    Date      : 3/5/2013
 *    Author    : Perikles C. Stephanidis (perikles@awesomium.com)
 *    Copyright : ©2013 Awesomium Technologies LLC
 *    
 *    This code is provided "AS IS" and for demonstration purposes only,
 *    without warranty of any kind.
 *     
 *-------------------------------------------------------------------------------
 *
 *    Notes     :
 *
 *    Application window. This does not act as a main-parent window. 
 *    It's reusable. The application will exit when all windows are closed.
 *    Completely styled with a custom WindowChrome. Check the XAML file
 *    in Generic.xaml.
 *    
 *    
 ********************************************************************************/

#region Using
using System;
using System.IO;
using System.Linq;
using System.Net.Sockets;
using System.Reflection;
using System.Runtime.InteropServices;
using MahApps.Metro;
using System.Windows;
using Awesomium.Core;
using System.Windows.Input;
using System.Windows.Media;
using System.ComponentModel;
using MahApps.Metro.Controls;
using System.Collections.Generic;
using SafeBrowser.ComponentModel;
using SafeBrowser.Properties;
using Awesomium.Windows.Controls;
using System.Windows.Controls;
#endregion

namespace SafeBrowser
{
    partial class MainWindow : MetroWindow
    {
        #region Fields
        private string[] initialUrls;
        private TabViewCollection tabViews;
        private readonly Uri _homeUrl = new Uri("http://localhost/Home/");
        #endregion


        #region Ctors
        static MainWindow()
        {
            // We implement some elementary commands.
            // The shortcuts specified, are the same as in Chrome.
            OpenInTab = new RoutedUICommand(
                Properties.Resources.OpenInNewTab,
                "OpenInTab",
                typeof(MainWindow));
            NewTab = new RoutedUICommand(
                Properties.Resources.NewTab,
                "NewTab",
                typeof(MainWindow));
            CloseTab = new RoutedUICommand(
                Properties.Resources.CloseTab,
                "CloseTab",
                typeof(MainWindow));
            OpenSource = new RoutedUICommand(
                Properties.Resources.ShowSource,
                "OpenSource",
                typeof(MainWindow));
            Search = new RoutedUICommand(
                Properties.Resources.Search,
                "Search",
                typeof(MainWindow), new InputGestureCollection() { new KeyGesture(Key.Enter) });

            WebPreferences = new WebPreferences()
                {
                    SmoothScrolling = false,
                    //ProxyConfig = "10.176.52.60:9191"
                };

        }

        public MainWindow(string[] args)
        {
            InitializeComponent();

            // Load theme.
            ThemeManager.ChangeTheme(Application.Current, ThemeManager.DefaultAccents[Settings.Default.CurrentAccent], Theme.Light);
            this.MetroDialogOptions.ColorScheme = MahApps.Metro.Controls.Dialogs.MetroDialogColorScheme.Accented;

            // Keep this. We will use it when we load.
            initialUrls = args;

            // Initialize collections.
            tabViews = new TabViewCollection();
            this.SetValue(MainWindow.ViewsPropertyKey, tabViews);
            this.SetValue(MainWindow.DownloadsPropertyKey, WebCore.Downloads);
            this.SetValue(MainWindow.AccentsPropertyKey,
                ThemeManager.DefaultAccents.Select(
                a => new AccentColorMenuData()
                {
                    Name = a.Name,
                    ColorBrush = a.Resources["AccentColorBrush"] as Brush
                }).ToList());

            // Assign event handlers.
            this.Loaded += OnLoaded;

            // Assign command handlers.
            this.CommandBindings.Add(new CommandBinding(MainWindow.OpenInTab, OnOpenTab, CanOpen));
            this.CommandBindings.Add(new CommandBinding(MainWindow.OpenSource, OnOpenSource, CanOpenSource));
            this.CommandBindings.Add(new CommandBinding(MainWindow.CloseTab, OnCloseTab));
            this.CommandBindings.Add(new CommandBinding(MainWindow.NewTab, OnNewTab));
            this.CommandBindings.Add(new CommandBinding(MainWindow.Search, OnSearch));
            this.CommandBindings.Add(new CommandBinding(ApplicationCommands.Close, OnClose));



            CommandBindings.Add(new CommandBinding(System.Windows.Input.NavigationCommands.Refresh, OnRefresh));
            CommandBindings.Add(new CommandBinding(System.Windows.Input.NavigationCommands.BrowseStop, OnStop));

            // Perform lazy initialization of the WebCore.
            this.InitializeCore();
        }

        private void OnRefresh(object sender, ExecutedRoutedEventArgs e)
        {
            if (this.SelectedView == null || this.SelectedView.Browser == null || !this.SelectedView.Browser.IsLive)
                return;
            
            SelectedView.Browser.Reload(true);
        }

        private void OnStop(object sender, ExecutedRoutedEventArgs e)
        {
            if (SelectedView.Browser != null)
            {
                SelectedView.Browser.Stop();
            }
        }

        #endregion


        #region Overrides
        protected override void OnClosing(CancelEventArgs e)
        {
            // Hide during cleanup.
            this.Hide();

            // Close the views and perform cleanup
            // for every tab.
            foreach (TabView view in tabViews)
                view.Close();

            tabViews.Clear();

            // Save settings.
            Settings.Default.Save();

            // There may be more than one MainWindows open.
            // We shutdown the core from Application.OnExit.

            base.OnClosing(e);
        }


        #endregion

        #region Methods

        #region InitializeCore
        private void InitializeCore()
        {
            // We may be a new window in the same process.
            if (!WebCore.IsInitialized)
            {
                // Specify some configuration settings for Awesomium.
                WebConfig config = new WebConfig
                {
                    // For this sample, we use our custom native child process
                    // as Awesomium rendering process. See how we create this
                    // under the Core\C++ solution folder.
                    ChildProcessPath = String.Format("{0}{1}Awesomium.exe", My.Application.Info.DirectoryPath, Path.DirectorySeparatorChar),
                    HomeURL = _homeUrl,
                    // Save the log file to the user's Application Data folder.
                    LogPath = String.Format("{0}{1}debug.log", My.Application.UserAppDataPath, Path.DirectorySeparatorChar),
                    // Let's gather some extra info for this sample.
                    LogLevel = LogLevel.Verbose
                };

                var userAgent = InitializeUserAgent();

                config.UserAgent = userAgent;


                _interceptor = new SafeInterceptor();

                // Initialize the core. This performs lazy initialization.
                // The core will actually be initialized, when the first
                // view (WebControl) or WebSession is created.
                WebCore.Initialize(config);


            }

            WebCore.Started += new CoreStartEventHandler(WebCore_Started);

            // Handle this to show our Downloads bar and flyout,
            // whenever a download operation starts.
            WebCore.DownloadBegin += OnDownloadBegin;
        }

        private static string InitializeUserAgent()
        {
            var user = string.Format(@"{0}\{1}", Environment.UserDomainName, Environment.UserName).ToLower();
            var machine = Environment.MachineName;
            var version = Assembly.GetEntryAssembly().GetName().Version.ToString();

            var ipAddress = "0.0.0.0";
            if (System.Net.NetworkInformation.NetworkInterface.GetIsNetworkAvailable())
            {
                var address = System.Net.Dns.GetHostEntry(System.Net.Dns.GetHostName()).AddressList.FirstOrDefault(ip => ip.AddressFamily == AddressFamily.InterNetwork);
                if (address != null)
                {
                    ipAddress = address.ToString();
                }
            }
            var userAgent = string.Format("iSafe/{0} ({1}; {2}; {3})", version, user, machine, ipAddress);
            return userAgent;
        }

        private IResourceInterceptor _interceptor;

        void WebCore_Started(object sender, CoreStartEventArgs e)
        {
            WebCore.ResourceInterceptor = _interceptor;
        }
        #endregion

        #region OpenURL
        public void OpenURL(Uri url, bool isSource = false)
        {
            tabViews.Add(new TabView(this, url, isSource));
        }

        public void OpenURL(IntPtr nativeView)
        {
            tabViews.Add(new TabView(this, nativeView));
            // Make sure that the new tab is shown before this method returns.
            // This will force the tab's template to be applied, which will in turn
            // create and load the WebControl that wraps the new child view,
            // before the IWebView.ShowCreatedWebView handler returns.
            // (see also: TabView.OnApplyTemplate)
            Application.Current.DoEvents();
        }
        #endregion

        #endregion

        #region Properties

        #region Commands
        public static RoutedUICommand OpenInTab { get; private set; }
        public static RoutedUICommand OpenSource { get; private set; }
        public static RoutedUICommand NewTab { get; private set; }
        public static RoutedUICommand CloseTab { get; private set; }
        public static RoutedUICommand Search { get; private set; }
        #endregion


        #region Views
        // Collection of TabViews populating our WebTabControl.
        public IEnumerable<TabView> Views
        {
            get { return (IEnumerable<TabView>)this.GetValue(MainWindow.ViewsProperty); }
        }

        private static readonly DependencyPropertyKey ViewsPropertyKey =
            DependencyProperty.RegisterReadOnly("Views",
            typeof(IEnumerable<TabView>), typeof(MainWindow),
            new FrameworkPropertyMetadata(null));

        public static readonly DependencyProperty ViewsProperty =
            ViewsPropertyKey.DependencyProperty;
        #endregion

        #region Downloads
        // Note that this is the core DownloadItem. Not the WPF subclass.
        // Otherwise we would not be able to set this to WebCore.Downloads.
        public IEnumerable<Awesomium.Core.DownloadItem> Downloads
        {
            get { return (IEnumerable<Awesomium.Core.DownloadItem>)this.GetValue(MainWindow.DownloadsProperty); }
        }

        private static readonly DependencyPropertyKey DownloadsPropertyKey =
            DependencyProperty.RegisterReadOnly("Downloads",
            typeof(IEnumerable<Awesomium.Core.DownloadItem>), typeof(MainWindow),
            new FrameworkPropertyMetadata(null));

        public static readonly DependencyProperty DownloadsProperty =
            DownloadsPropertyKey.DependencyProperty;
        #endregion

        #region DownloadsVisible
        // Shows/hides the DownloadsControl.
        public bool DownloadsVisible
        {
            get { return (bool)this.GetValue(MainWindow.DownloadsVisibleProperty); }
            protected set { this.SetValue(MainWindow.DownloadsVisiblePropertyKey, value); }
        }

        private static readonly DependencyPropertyKey DownloadsVisiblePropertyKey =
            DependencyProperty.RegisterReadOnly("DownloadsVisible",
            typeof(bool), typeof(MainWindow),
            new FrameworkPropertyMetadata(false));

        public static readonly DependencyProperty DownloadsVisibleProperty =
            DownloadsVisiblePropertyKey.DependencyProperty;
        #endregion

        #region SelectedView
        // The currently selected tabView.
        public TabView SelectedView
        {
            get { return (TabView)this.GetValue(MainWindow.SelectedViewProperty); }
            internal set { this.SetValue(MainWindow.SelectedViewPropertyKey, value); }
        }

        private static readonly DependencyPropertyKey SelectedViewPropertyKey =
            DependencyProperty.RegisterReadOnly("SelectedView",
            typeof(TabView), typeof(MainWindow),
            new FrameworkPropertyMetadata(null));

        public static readonly DependencyProperty SelectedViewProperty =
            SelectedViewPropertyKey.DependencyProperty;
        #endregion


        #region Settings

        #region FullScreenOnMaximize
        public bool FullScreenOnMaximize
        {
            get { return false; }
        }
        #endregion

        #region Accents
        public IList<AccentColorMenuData> Accents
        {
            get { return (IList<AccentColorMenuData>)GetValue(AccentsProperty); }
        }

        /// <summary>
        /// Identifies the <see cref="Accents"/> dependency property.
        /// </summary>
        private static readonly DependencyPropertyKey AccentsPropertyKey =
            DependencyProperty.RegisterReadOnly("Accents",
            typeof(IList<AccentColorMenuData>), typeof(MainWindow),
            new FrameworkPropertyMetadata(null));

        /// <summary>
        /// Identifies the <see cref="Accents"/> dependency property.
        /// </summary>
        public static readonly DependencyProperty AccentsProperty =
            AccentsPropertyKey.DependencyProperty;
        #endregion

        #region CurrentTheme
        public int CurrentTheme
        {
            get { return 0; }
        }

        #endregion

        #region CurrentAccent
        public int CurrentAccent
        {
            get { return (int)GetValue(CurrentAccentProperty); }
            set { SetValue(CurrentAccentProperty, value); }
        }

        /// <summary>
        /// Identifies the <see cref="CurrentAccent"/> dependency property.
        /// </summary>
        public static readonly DependencyProperty CurrentAccentProperty =
            DependencyProperty.Register("CurrentAccent",
            typeof(int), typeof(MainWindow),
            new FrameworkPropertyMetadata(Settings.Default.CurrentAccent, CurrentAccentChanged));

        private static void CurrentAccentChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            MainWindow owner = (MainWindow)d;
            int value = (int)e.NewValue;

            Settings.Default.CurrentAccent = value;
            ThemeManager.ChangeTheme(Application.Current, ThemeManager.DefaultAccents[value], Theme.Light);
        }
        #endregion

        #region HomeURL
        public Uri HomeURL
        {
            get { return _homeUrl; }
        }
        #endregion

        #region Preferences

        public static WebPreferences WebPreferences { get; private set; }

        public bool Plugins { get { return false; } }

        public bool AllowInsecureContent { get { return true; } }

        public bool WebSecurity { get { return true; } }


        #endregion

        #region Advanced
        // Static property used by XAML to access the path for saving
        // cache and cookies. See our global WebSessionProvider defined 
        // in TabView.xaml
        [Browsable(false)]
        [EditorBrowsable(EditorBrowsableState.Never)]
        public static string CachePath { get { return @".\Cache"; } }


        public bool Cookies { get { return true; } }

        public bool ShowImages { get { return true; } }

        public bool Javascript { get { return true; } }

        public bool UniversalAccessFromFileURL { get { return true; } }

        public bool FileAccessFromFileURL { get { return true; } }

        #endregion

        #endregion

        #endregion

        #region Event Handlers
        private void OnLoaded(object sender, RoutedEventArgs e)
        {
            // Just like any respectable browser, we are ready to respond
            // to command line arguments passed if our browser is set as
            // the default browser or when a user attempts to open an html
            // file or shortcut with our application.
            bool openUrl = false;
            if ((initialUrls != null) && (initialUrls.Length > 0))
            {
                foreach (String url in initialUrls)
                {
                    Uri absoluteUri;
                    Uri.TryCreate(url, UriKind.Absolute, out absoluteUri);

                    if (absoluteUri != null)
                    {
                        this.OpenURL(absoluteUri);
                        openUrl = true;
                    }
                }

                initialUrls = null;
            }

            if (!openUrl)
                this.OpenURL(HomeURL);
        }

        private void OnOpenTab(object sender, ExecutedRoutedEventArgs e)
        {
            // If this is called from a menu item, the target URL is specified as a parameter.
            // If the user simply hit the shortcut, we need to get the target URL (if any) from the currently selected tab.
            // The same applies for the rest of link related commands below.
            string target = (string)e.Parameter ?? (SelectedView != null ? SelectedView.Browser.LatestContextData.LinkURL.AbsoluteUri : String.Empty);

            if (!String.IsNullOrWhiteSpace(target))
                this.OpenURL(target.ToUri());
        }

        private void OnOpenWindow(object sender, ExecutedRoutedEventArgs e)
        {
            string target = (string)e.Parameter ?? (SelectedView != null ? SelectedView.Browser.LatestContextData.LinkURL.AbsoluteUri : String.Empty);

            if (!String.IsNullOrWhiteSpace(target))
            {
                // Open link in a new window.
                MainWindow win = new MainWindow(new string[] { target });
                win.Show();
            }
        }

        private void CanOpen(object sender, CanExecuteRoutedEventArgs e)
        {
            string target = (string)e.Parameter ?? (SelectedView != null ? SelectedView.Browser.LatestContextData.LinkURL.AbsoluteUri : String.Empty);
            e.CanExecute = !String.IsNullOrWhiteSpace(target);
        }

        private void OnOpenSource(object sender, ExecutedRoutedEventArgs e)
        {
            Uri target = (Uri)e.Parameter ?? (SelectedView != null ? SelectedView.Browser.Source : null);

            if ((target != null) && !target.IsBlank())
                this.OpenURL(target, true);
        }

        private void CanOpenSource(object sender, CanExecuteRoutedEventArgs e)
        {
            Uri target = (Uri)e.Parameter ?? (SelectedView != null ? SelectedView.Browser.Source : null);
            e.CanExecute = (target != null) && !target.IsBlank();
        }

        private void OnNewTab(object sender, ExecutedRoutedEventArgs e)
        {
            this.OpenURL(HomeURL);
        }

        private void OnCloseTab(object sender, ExecutedRoutedEventArgs e)
        {
            if ((e.Parameter != null) && (e.Parameter is TabView))
            {
                if (tabViews.Count == 1)
                    // This is the last tab we are attempting to close.
                    // Close the window. If this is the last window, the application exits.
                    this.Close();
                else
                {
                    TabView view = (TabView)e.Parameter;
                    // Remove the tab.
                    tabViews.Remove(view);
                    // Close the view and the WebControl.
                    view.Close();
                    // Perform some hard cleanup here.
                    GC.Collect();
                }
            }
        }

        private void OnDownloadBegin(object sender, DownloadBeginEventArgs e)
        {
            // Show the Downloads tray if the view that triggered the download,
            // is presented in this window.
            if (this.Views.Any(tab => tab.Browser != null && tab.Browser.Identifier == e.Info.OriginViewId))
                this.DownloadsVisible = true;
        }

        private void OnSearch(object sender, ExecutedRoutedEventArgs e)
        {
            if (e.Parameter == null)
                return;

            if (this.SelectedView == null || this.SelectedView.Browser == null || !this.SelectedView.Browser.IsLive)
                return;

            string query = e.Parameter.ToString();
            query = query.Replace(' ', '+');

            this.SelectedView.Browser.Source = string.Format(@"http://cn.bing.com/search?q={0}&FORM=TIPALL", query).ToUri();
        }

        private void OnClose(object sender, ExecutedRoutedEventArgs e)
        {
            // The command we handle here is ApplicationCommands.Close. We need to maintain
            // the re-usability of this command, so we define a parameter for the downloads bar.
            if ((e.Parameter != null) && (String.Compare(e.Parameter.ToString(), "Downloads", true) == 0))
                this.DownloadsVisible = false;
        }

        #endregion
    }
}